struct alphabet {
	int pid;
	char** actions;
};

struct queue {
	char action[15];
	struct process *firstProces;
	struct queue *nextQueue;
};
struct process {
	int pid;
	int isReady;
	int endpoint;
	struct process* nextProcess;
};

#define NO_OF_ALPHABETES	7
#define NO_OF_ACTIONS		10

int find_empty_index_by_linear_prob(struct alphabet *, int);
struct alphabet alphabetList[NO_OF_ALPHABETES];
void initializeAlphabetList(struct alphabet *);
void initializeactionList(struct queue*);

void insertProcesByAction(struct queue* action_list, char* newAction, int pid, int endpoint) {
	int i = 0;
	int x = 0;
	struct process* newProcess = (struct process*) malloc(sizeof(struct process));
	struct process* process;

	/* Loop through action list */
	while (strcmp(action_list[i].action, "") != 0) {
		/* Action already in list */
		if (strcmp(action_list[i].action, newAction) == 0) {
			process = action_list[i].firstProces;
			/* Going to the end of the process list */
			while (process->nextProcess != NULL) {
				process = process->nextProcess;
			}

			/* Set new process properties */
			newProcess->pid = pid;
			newProcess->endpoint = endpoint;
			newProcess->nextProcess = NULL;
			newProcess->isReady = 0;

			/* Adding new process to the end of process list */
			process->nextProcess = newProcess;
			return;
		}
		i++;
	}

	/*Action not yet in list*/
	while(newAction[x] != '\0')
	{
		/* Adding new action to end of the list */
		action_list[i].action[x] = newAction[x];
		x++;
	}

	 /* Add EOS */
	action_list[i].action[x] = newAction[x];

	/* Set new process properties */
	newProcess->pid = pid;
	newProcess->endpoint = endpoint;
	newProcess->nextProcess = NULL;
	newProcess->isReady = 0;

	/* Make added process the first process */
	action_list[i].firstProces = newProcess;

	/* Make previous last queue to new queue */
	if (i != 0) {
		action_list[i - 1].nextQueue = &action_list[i];
	}
	return;
}

int hashIndex(int PID) {
	return (PID % NO_OF_ALPHABETES);
}

void insertProcesList(struct queue* action_list, struct alphabet newAlphabet, int endpoint) {
	int i = 0;
	/* Loop through alphabet and add all actions */
	while (strcmp(newAlphabet.actions[i], ";") != 0) {
		insertProcesByAction(action_list,newAlphabet.actions[i], newAlphabet.pid, endpoint);
		i++;
	}
}

void insertAlphabet(struct alphabet *alphabetList, struct alphabet newAlphabet) {
	int arrayIndex;

	/*Get an empty slot*/
	arrayIndex = find_empty_index_by_linear_prob(alphabetList, newAlphabet.pid);
	alphabetList[arrayIndex] = newAlphabet;
}

int find_empty_index_by_linear_prob(struct alphabet* alphabetList, int PID) {
	int i, hashedIndex, initArrayIndex;
	initArrayIndex = hashIndex(PID);

	/*Find an empty slot*/
	for (i = 0; i < NO_OF_ALPHABETES; i++) {

		/*If pid =-1 then the arrayslot has never been used, so it's empty */
		hashedIndex = hashIndex(i + initArrayIndex);
		if (alphabetList[hashedIndex].pid == -1) {
			return hashedIndex;
		}

	}

	/*If arrayindex can NOT be found the array is full */
	return -1;
}

int askForGo(struct queue * action_list,char* action, int pid, int cs_endpoint) {
	int s, sendFlag;
	message m, reply;
	char *argument;
	struct process* newProcess;
	struct queue* newQueue;
	struct queue* currentqueue;
	struct process* process;
	int allProcessesReady = 1;
	int k = 0;
	currentqueue = action_list;

	/*Loop through the entire action list*/
	while (currentqueue != NULL && strcmp(currentqueue->action, "") != 0) {
		/*Action found in action list*/
		if (strcmp(currentqueue->action, action) == 0) {
			process = currentqueue->firstProces;

			/*Checking if all processes are ready*/
			while (process != NULL) {
				if (process->pid == pid) {
					/*Setting the ready flag of the process*/
					process->isReady = 1;
				}
				if (process->isReady != 1) {
					/*Not all processes are ready for a GO*/
					allProcessesReady = 0;
				}
				/* Step to next process to ensure a loop */
				process = process->nextProcess;
			}
			if (allProcessesReady == 1) {
				process = currentqueue->firstProces;
				/*Split action string at dot and send messages*/
				argument = strtok(action, ".");

				/*Declare message properties with default value*/
				m.m_type = COMMAND_TO_CS;
				m.CS_CMD = CS_NO_VAL;
				m.CS_ARG = CS_NO_VAL;
				m.CS_VAL = CS_NO_VAL;


				/* Loop through all arguments*/
				while (argument != NULL) {
					switch (k) {
						case 0: m.CS_CMD = argument[0]; break;
						case 1: m.CS_ARG = argument[0]; break;
						case 2: m.CS_VAL = atoi(argument); break;
						default: sendFlag = -1; break;
					}

					argument = strtok(NULL, ".");
					k++;
				}
				if (sendFlag == -1 || OK != (s = send(cs_endpoint, &m))) {
					panic("Did not send command message from COM to CS.\n");
				}

				/* Sending GO to all processes waiting for this action */
				while (process != NULL) {
					if (OK != (s = send(process->endpoint, &reply))) {
						panic("Did not reply user process.\n");
					}
					/*Set isReady to 0 because process has been executed and switch to next state */
					process->isReady = 0;
					process = process->nextProcess;
				}
			}
			else {/* Make process wait x time */}
			return allProcessesReady;
		} else {
			/*Go to new queue*/
			currentqueue = currentqueue->nextQueue;
		}
	}
	panic("Action not known in COM driver.\n");
	return -1;
}

void initializeactionList(struct queue* actionList) {
	int i;
	for (i = 0; i < NO_OF_ACTIONS; i++) {
		actionList[i].action[0] = '\0';
		actionList[i].firstProces = NULL;
		actionList[i].nextQueue = NULL;
	}

}

void initializeAlphabetList(struct alphabet *alphabetList) {
	int i;
	for (i = 0; i < NO_OF_ALPHABETES; i++) {
		alphabetList[i].pid = -1;
		alphabetList[i].actions = NULL;
	}
}
